#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "defs.h"

// for line editor
typedef struct line
{
  char *content;
  int number;
  struct line *next;
  struct line *previous;
}Line;

typedef enum
  {
    ATOM,
    SEXP
  }ListType;

typedef enum
  {
    NORMAL,
    PARSEROOTNODE,
    PARSEREST
  }STATE;

typedef enum
  {
    LEFTPARENTHESIS,      //(
    RIGHTPARENTHESIS,     //)
    NAME,//name
    LEFTQUOTE,//'
    STRING,//"
    ACCENT,//`
    NUMBER//1.2 23 4 5 6
  }TokenType;

enum LispType
  {
    LispInt,
    LispSymbol,
    LispMisc,
    LispString,
    LispCons,
    LispFloat,
    LispVectorlike
  };

typedef union LispObject
{
  LispType type;
  union
  {
    int LispInt;
    double LispDouble;
    float LispFloat;
    char *LispString;
  }value;
}LispObject;

struct LispSubr
{
  int size;
  LispObject (*function)();
  short minArgs,maxArgs;
  char *symbolName;
  char *prompt;
  char *doc;
};

#define DEFUN(lname, fnname, sname, minargs, maxargs, prompt, doc)	\
  LispObject fnname ();						\
  struct LispSubr sname =                                               \
    {sizeof(struct LispSubr),                                           \
     fnname,minargs,maxargs,lname,prompt,0};                            \
  LispObject fnname

typedef struct token
{
  char *value;
  TokenType type;
}Token;

typedef struct list
{
  ListType type;
  union
    {
      Token *value;
      struct list *vPointer;
    }ListValue;
  struct list *next;
}List;

FILE *fp;
Line *editorRootNode;



Token *GetNameWithLineNumber(FILE *fp)
{
  
  return NULL;
}

Token *GetName(FILE *fp)
{
  char c;
  char name[40];
  int i = 0;
  memset(name,0,40);
  while ((c = fgetc(fp)) != EOF)
    {
      if(c == ' ')
        {
          break;
        }
      else
        {
          name[i++] = c;
        }
    }
  Token *p = (Token *)malloc(sizeof(Token));
  memset(p,0,sizeof(Token));
  p -> type = NAME;
  p -> value = name; 
  return p;
}

void Log()
{

}

Token *NewToken()
{
  Token *p = (Token *)malloc(sizeof(Token));
  if(p == NULL) 
    {
      Log();
      printf("Memory allocate failed.");
      exit(1);
    }
  memset(p,0,sizeof(Token));  
  return p;
}

List *NewList()
{
  List *p = (List *)malloc(sizeof(List));
  if(p == NULL) 
    {
      Log();
      printf("Memory allocate failed.");
      exit(1);
    }
  memset(p,0,sizeof(List));  
  return p;
}

char *NewChar(int size)
{
  char *p = (char *)malloc(sizeof(char)*size);
  if(*p == NULL) 
    {
      Log();
      printf("Memory allocate failed.");
      exit(1);
    }
  memset(p,0,sizeof(char)*size);
  return p;
}

Token* ParseName(char c,FILE *fp)
{
  char* symbolTable = NewChar(100);
  symbolTable[0] = c;

  Token* name = NewToken(); 

  int i = 1;
  while ((c = fgetc(fp)) != EOF)
    {
      if(c == ' '
         || c == '\t'
         || c == '\v'
         || c == '\f'
		 || c == '\n')
        {
          break;
        }

      if(c == ')') 
        {
          ungetc(c,fp);
          break;
        }

      symbolTable[i++] = c;
    }  

  name -> type = NAME;
  name -> value = symbolTable; 
  return name;
}

Token* ParseNumber(char c,FILE *fp)
{
  Token *t = NewToken();
  char *num = NewChar(100);
  char *p = num;
  num[0] = c;

  while ((c = fgetc(fp)) != EOF)
    {
      if((c-'0'>=0&&c-'9'<=0)||c=='.')
        {
          *p=c;
          p++;
        }
      else 
        {
          ungetc(c,fp);
          break;          
        }

    }

  t -> value = num;
  t -> type = NUMBER;
  return t;
}

Token *ParseString(FILE *fp)
{
  char c;
  char *LispString = NewChar(128);
  Token * t =  NewToken(); 
  int index = 0;
  while ((c = fgetc(fp)) != EOF)
    {
      if(c == '\"') 
        break;

      LispString[index++] = c;
    }

  t -> type = STRING;
  t -> value = LispString; 
  return t;
}

/*
  Get Token from source code files.
 */

Token* GetNextToken(FILE *fp)
{
  char c;
  Token *token =  (Token *)malloc(sizeof(Token));
  memset(token,0,sizeof(Token));
  
  while ((c = fgetc(fp)) != EOF)
    {
      //to do: print out symbols

      switch(c)
        {
          //various name, key word
        case 'a': case 'b': case 'c': case 'd': case 'e': 
        case 'f': case 'g': case 'h': case 'i': case 'j': 
        case 'k': case 'l': case 'm': case 'n': case 'o': 
        case 'p': case 'q': case 'r': case 's': case 't': 
        case 'u': case 'v': case 'w': case 'x': case 'y': 
        case 'z': 
        case 'A': case 'B': case 'C': case 'D': case 'E': 
        case 'F': case 'G': case 'H': case 'I': case 'J': 
        case 'K': case 'L': case 'M': case 'N': case 'O': 
        case 'P': case 'Q': case 'R': case 'S': case 'T': 
        case 'U': case 'V': case 'W': case 'X': case 'Y': 
        case 'Z':
        case '_':
        case '+':
        case '*':
        case '-':
        case '/':
          //parse name here
          return ParseName(c,fp);

          //numbers;
        case '0': case '1': case '2': case '3': case '4': case '5': 
        case '6': case '7': case '8': case '9':
          //parse number
          return ParseNumber(c,fp);
          //          break;

        case '\'':
          token -> type = LEFTQUOTE;
          token -> value = "\'";
          return token;
          
        case '\`':
          token -> type = ACCENT;
          token -> value = "\`"; 
          return token;
          
        case '\"':
          return ParseString(fp);

          //case '\t':
        case '(':
          token -> type = LEFTPARENTHESIS;
          token -> value = "("; 
          return token;
          
        case ')':
          token -> type = RIGHTPARENTHESIS;
          token -> value = ")"; 
          return token;
          
        case '\n':
          
          break;
        }

    }
  
  // The end of file, token will be NULL
  return NULL;
}
/*
  <int inList> stand for the start state of automation. </>
  <FILE *fp>  the current open files</>
 */
List* BuildChain(int inList,FILE *fp)
{
  List *root ;
  List *p;
  List *q;
  STATE state=NORMAL;
  if(inList == 1)
    state = PARSEROOTNODE;
    
  Token *c;
  while((c = GetNextToken(fp)) != NULL)
    {
      switch(state)
        {
        case NORMAL:
          if(c -> type == LEFTPARENTHESIS)   
            {
              state = PARSEROOTNODE; 
            }
          break;
        case PARSEROOTNODE:
          {
            root =  NewList();
            if(c -> type == LEFTPARENTHESIS) 
              {
                root -> type = SEXP;
                root -> ListValue.vPointer = BuildChain(1,fp);
              }
            else
              {
                root -> type = ATOM;
                root -> ListValue.value = c;
              }
            p = root;
            state = PARSEREST;
          }
          break;
        
        case PARSEREST:

          if(c->type == LEFTPARENTHESIS) 
            {
              q = NewList();              
              q -> type = SEXP;
              q -> ListValue.vPointer = BuildChain(1,fp); 
			  p->next=q;
			  p=p->next;
            }
          else if(c->type == RIGHTPARENTHESIS) 
            {
              //state = NORMAL;
              return root;
            }
          else
            {
              //q = new List();
              q = NewList();
              q -> type = ATOM;
              q -> ListValue.value = c;
              p -> next = q;
              p = p -> next;
            }
          break;
        }
    }
  return root;
}

int main() 
{ 

#ifdef FILEIO
  //  freopen("in.txt","r",stdin); 
  freopen("out.txt","w",stdout); 
#endif

  char c;

  
  fp = fopen("in.txt","r");
  //editorRootNode = (Line *)malloc(sizeof(Line));
  
  List *root = BuildChain(0,fp);
  


#ifdef FILEIO
  //fclose(stdin);
  fclose(stdout);
#endif  
  
  return 0; 
}
